package org.jenkinsci.plugins.p4scm;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.jenkinsci.plugins.p4scm.utils.CommonUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.tek42.perforce.model.Changelist;

import hudson.model.AbstractBuild;
import hudson.scm.ChangeLogParser;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;

public class P4ChangeLogParser extends ChangeLogParser {

    public P4ChangeLogParser() {
        super();
    }
    
    @SuppressWarnings("rawtypes")
    @Override
    public ChangeLogSet<? extends Entry> parse(AbstractBuild build,
            File changelogFile) throws IOException, SAXException {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            ChangeLogHandler handler = new ChangeLogHandler(build);
            parser.parse(changelogFile, handler);
            return handler.getChangeLogSet();
        } catch (Exception e) {
            throw new SAXException("Could not parse perforce changelog: ",e);
        }
    }



    @SuppressWarnings({"rawtypes", "unchecked"})
    public static class ChangeLogHandler extends DefaultHandler {
        
        
        private Stack objects = new Stack();
        private StringBuffer text = new StringBuffer();

        private List<P4ChangeLogEntry> changeLogEntries = null;
        private P4ChangeLogSet changeLogSet = null;
        private AbstractBuild<?,?> build = null;
        
        public ChangeLogHandler(AbstractBuild<?,?> build) {
            this.build = build;
        }

        public P4ChangeLogSet getChangeLogSet() {
            return changeLogSet;
        }
        
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            text.append(ch, start, length);
        }

        @Override
        public void endDocument() throws SAXException {
        }

        
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equalsIgnoreCase("changelog")) {
                //this is the root, so don't do anything
                return;
            }
            if (objects.peek() instanceof Changelist) {
                Changelist changelist = (Changelist) objects.peek();
                if (qName.equalsIgnoreCase("changenumber")) {
                    changelist.setChangeNumber(new Integer(text.toString()));
                    return;
                }
                if (qName.equalsIgnoreCase("date")) {
                    changelist.setDate(CommonUtil.stringDateToJavaDate(text.toString()));
                    return;
                }
                if (qName.equalsIgnoreCase("description")) {
                    changelist.setDescription(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("user")) {
                    changelist.setUser(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("workspace")) {
                    changelist.setWorkspace(text.toString());
                    return;
                }
            }
            if (objects.peek() instanceof Changelist.JobEntry) {
                Changelist.JobEntry job = (Changelist.JobEntry) objects.peek();
                if (qName.equalsIgnoreCase("name")) {
                    job.setJob(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("description")) {
                    job.setDescription(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("status")) {
                    job.setStatus(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("job")) {
                    objects.pop();
                    List joblist = (List) objects.peek();
                    joblist.add(job);
                    return;
                }
            }
            if (objects.peek() instanceof Changelist.FileEntry) {
                Changelist.FileEntry file = (Changelist.FileEntry) objects.peek();
                if (qName.equalsIgnoreCase("name")) {
                    file.setFilename(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("workspacePath")) {
                    file.setWorkspacePath(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("action")) {
                    file.setAction(Changelist.FileEntry.Action.valueOf(text.toString()));
                    return;
                }
                if (qName.equalsIgnoreCase("rev")) {
                    file.setRevision(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("changenumber")) {
                    file.setChangenumber(text.toString());
                    return;
                }
                if (qName.equalsIgnoreCase("file")) {
                    objects.pop();
                    List filelist = (List) objects.peek();
                    filelist.add(file);
                    return;
                }
            }
            if (qName.equalsIgnoreCase("files")) {
                ArrayList<Changelist.FileEntry> files = (ArrayList<Changelist.FileEntry>) objects.pop();
                Changelist changelist = (Changelist) objects.peek();
                changelist.setFiles(files);
                return;
            }
            if (qName.equalsIgnoreCase("jobs")) {
                ArrayList<Changelist.JobEntry> jobs = (ArrayList<Changelist.JobEntry>) objects.pop();
                Changelist changelist = (Changelist) objects.peek();
                changelist.setJobs(jobs);
                return;
            }
            if (qName.equalsIgnoreCase("entry")) {
                Changelist changelist = (Changelist) objects.pop();
                P4ChangeLogEntry entry = new P4ChangeLogEntry(changeLogSet);
                entry.setChange(changelist);
                changeLogEntries.add(entry);
                return;
            }
        }

        @Override
        public void startDocument() throws SAXException {
            changeLogEntries = new ArrayList<P4ChangeLogEntry>();
            changeLogSet = new P4ChangeLogSet(build, changeLogEntries);
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            text.setLength(0);

            if (qName.equalsIgnoreCase("changelog")) {
                //this is the root, so don't do anything
                return;
            }
            if (qName.equalsIgnoreCase("entry")) {
                objects.push(new Changelist());
                return;
            }
            if (objects.peek() instanceof Changelist) {
                if (qName.equalsIgnoreCase("files")) {
                    objects.push(new ArrayList<Changelist.FileEntry>());
                    return;
                }
                if (qName.equalsIgnoreCase("jobs")) {
                    objects.push(new ArrayList<Changelist.JobEntry>());
                    return;
                }
            }
            if (qName.equalsIgnoreCase("job")) {
                objects.push(new Changelist.JobEntry());
                return;
            }
            if (qName.equalsIgnoreCase("file")) {
                objects.push(new Changelist.FileEntry());
                return;
            }
        }
        
    }
}
